#include "gt9147.h"
#include "ctiic.h"
#include "string.h"
#include "lcd.h"

// IO操作函数
#define GT_RST(n) (n ? HAL_GPIO_WritePin(T_IIC_PORT, T_RST_PIN, GPIO_PIN_SET) : HAL_GPIO_WritePin(T_IIC_PORT, T_RST_PIN, GPIO_PIN_RESET)) // GT9147复位引脚
#define GT_INT    HAL_GPIO_ReadPin(T_IIC_PORT, T_INT_PIN)                                                                                 // GT9147中断引脚

// I2C读写命令
#define GT_CMD_WR 0X28 // 写命令
#define GT_CMD_RD 0X29 // 读命令

// GT9147 部分寄存器定义
#define GT_CTRL_REG  0X8040 // GT9147控制寄存器
#define GT_CFGS_REG  0X8047 // GT9147配置起始地址寄存器
#define GT_CHECK_REG 0X80FF // GT9147校验和寄存器
#define GT_PID_REG   0X8140 // GT9147产品ID寄存器

#define GT_GSTID_REG 0X814E // GT9147当前检测到的触摸情况
#define GT_TP1_REG   0X8150 // 第一个触摸点数据地址
#define GT_TP2_REG   0X8158 // 第二个触摸点数据地址
#define GT_TP3_REG   0X8160 // 第三个触摸点数据地址
#define GT_TP4_REG   0X8168 // 第四个触摸点数据地址
#define GT_TP5_REG   0X8170 // 第五个触摸点数据地址

struct gt9147
{
  uint8_t sta;
  uint8_t touchtype;
  uint8_t x[10];
  uint8_t y[10];
};

static struct gt9147 tp_dev={0,0x01,{0},{0}};


// GT9147配置参数表
// 第一个字节为版本号(0X60),必须保证新的版本号大于等于GT9147内部
// flash原有版本号,才会更新配置.
const uint8_t GT9147_CFG_TBL[] =
    {
        0X60,
        0XE0,
        0X01,
        0X20,
        0X03,
        0X05,
        0X35,
        0X00,
        0X02,
        0X08,
        0X1E,
        0X08,
        0X50,
        0X3C,
        0X0F,
        0X05,
        0X00,
        0X00,
        0XFF,
        0X67,
        0X50,
        0X00,
        0X00,
        0X18,
        0X1A,
        0X1E,
        0X14,
        0X89,
        0X28,
        0X0A,
        0X30,
        0X2E,
        0XBB,
        0X0A,
        0X03,
        0X00,
        0X00,
        0X02,
        0X33,
        0X1D,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X32,
        0X00,
        0X00,
        0X2A,
        0X1C,
        0X5A,
        0X94,
        0XC5,
        0X02,
        0X07,
        0X00,
        0X00,
        0X00,
        0XB5,
        0X1F,
        0X00,
        0X90,
        0X28,
        0X00,
        0X77,
        0X32,
        0X00,
        0X62,
        0X3F,
        0X00,
        0X52,
        0X50,
        0X00,
        0X52,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X0F,
        0X0F,
        0X03,
        0X06,
        0X10,
        0X42,
        0XF8,
        0X0F,
        0X14,
        0X00,
        0X00,
        0X00,
        0X00,
        0X1A,
        0X18,
        0X16,
        0X14,
        0X12,
        0X10,
        0X0E,
        0X0C,
        0X0A,
        0X08,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X29,
        0X28,
        0X24,
        0X22,
        0X20,
        0X1F,
        0X1E,
        0X1D,
        0X0E,
        0X0C,
        0X0A,
        0X08,
        0X06,
        0X05,
        0X04,
        0X02,
        0X00,
        0XFF,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0X00,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
        0XFF,
};
// 发送GT9147配置参数
// mode:0,参数不保存到flash
//      1,参数保存到flash
uint8_t GT9147_Send_Cfg(uint8_t mode)
{
  uint8_t buf[2];
  uint8_t i = 0;
  buf[0]    = 0;
  buf[1]    = mode;                                                         // 是否写入到GT9147 FLASH?  即是否掉电保存
  for (i = 0; i < sizeof(GT9147_CFG_TBL); i++) buf[0] += GT9147_CFG_TBL[i]; // 计算校验和
  buf[0] = (~buf[0]) + 1;
  GT9147_WR_Reg(GT_CFGS_REG, (uint8_t *)GT9147_CFG_TBL, sizeof(GT9147_CFG_TBL)); // 发送寄存器配置
  GT9147_WR_Reg(GT_CHECK_REG, buf, 2);                                           // 写入校验和,和配置更新标记
  return 0;
}
// 向GT9147写入一次数据
// reg:起始寄存器地址
// buf:数据缓缓存区
// len:写数据长度
// 返回值:0,成功;1,失败.
uint8_t GT9147_WR_Reg(uint16_t reg, uint8_t *buf, uint8_t len)
{
  uint8_t i;
  uint8_t ret = 0;
  CT_IIC_Start();
  CT_IIC_Send_Byte(GT_CMD_WR); // 发送写命令
  CT_IIC_Wait_Ack();
  CT_IIC_Send_Byte(reg >> 8); // 发送高8位地址
  CT_IIC_Wait_Ack();
  CT_IIC_Send_Byte(reg & 0XFF); // 发送低8位地址
  CT_IIC_Wait_Ack();
  for (i = 0; i < len; i++) {
    CT_IIC_Send_Byte(buf[i]); // 发数据
    ret = CT_IIC_Wait_Ack();
    if (ret) break;
  }
  CT_IIC_Stop(); // 产生一个停止条件
  return ret;
}
// 从GT9147读出一次数据
// reg:起始寄存器地址
// buf:数据缓缓存区
// len:读数据长度
void GT9147_RD_Reg(uint16_t reg, uint8_t *buf, uint8_t len)
{
  uint8_t i;
  CT_IIC_Start();
  CT_IIC_Send_Byte(GT_CMD_WR); // 发送写命令
  CT_IIC_Wait_Ack();
  CT_IIC_Send_Byte(reg >> 8); // 发送高8位地址
  CT_IIC_Wait_Ack();
  CT_IIC_Send_Byte(reg & 0XFF); // 发送低8位地址
  CT_IIC_Wait_Ack();
  CT_IIC_Start();
  CT_IIC_Send_Byte(GT_CMD_RD); // 发送读命令
  CT_IIC_Wait_Ack();
  for (i = 0; i < len; i++) {
    buf[i] = CT_IIC_Read_Byte(i == (len - 1) ? 0 : 1); // 发数据
  }
  CT_IIC_Stop(); // 产生一个停止条件
}
// 初始化GT9147触摸屏
// 返回值:0,初始化成功;1,初始化失败
uint8_t GT9147_Init(void)
{
  uint8_t temp[5];
  GPIO_InitTypeDef GPIO_Initure;

  T_IIC_PORT_ENABLE(); // 开启GPIOG时钟

  // PH7
  GPIO_Initure.Pin   = T_INT_PIN;           // PH7
  GPIO_Initure.Mode  = GPIO_MODE_INPUT;     // 输入
  GPIO_Initure.Pull  = GPIO_PULLUP;         // 上拉
  GPIO_Initure.Speed = GPIO_SPEED_HIGH;     // 高速
  HAL_GPIO_Init(T_IIC_PORT, &GPIO_Initure); // 初始化

  // PI8
  GPIO_Initure.Pin  = T_RST_PIN;            // PI8
  GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;  // 推挽输出
  HAL_GPIO_Init(T_IIC_PORT, &GPIO_Initure); // 初始化
  CT_IIC_Init();                            // 初始化电容屏的I2C总线
  GT_RST(0);                                // 复位
  delay_ms(10);
  GT_RST(1); // 释放复位
  delay_ms(10);
  GPIO_Initure.Pin   = T_INT_PIN;           // PH7
  GPIO_Initure.Mode  = GPIO_MODE_INPUT;     // 输入
  GPIO_Initure.Pull  = GPIO_NOPULL;         // 不带上下拉，浮空输入
  GPIO_Initure.Speed = GPIO_SPEED_HIGH;     // 高速
  HAL_GPIO_Init(T_IIC_PORT, &GPIO_Initure); // 初始化
  delay_ms(100);
  GT9147_RD_Reg(GT_PID_REG, temp, 4); // 读取产品ID
  temp[4] = 0;
  printf("CTP ID:%s\r\n", temp);         // 打印ID
  if (strcmp((char *)temp, "9147") == 0) // ID==9147
  {
    temp[0] = 0X02;
    GT9147_WR_Reg(GT_CTRL_REG, temp, 1); // 软复位GT9147
    GT9147_RD_Reg(GT_CFGS_REG, temp, 1); // 读取GT_CFGS_REG寄存器
    if (temp[0] < 0X60)                  // 默认版本比较低,需要更新flash配置
    {
      printf("Default Ver:%d\r\n", temp[0]);
      if (lcddev.id == 0X5510) GT9147_Send_Cfg(1); // 更新并保存配置
    }
    delay_ms(10);
    temp[0] = 0X00;
    GT9147_WR_Reg(GT_CTRL_REG, temp, 1); // 结束复位
    return 0;
  }
  return 1;
}
const uint16_t GT9147_TPX_TBL[5] = {GT_TP1_REG, GT_TP2_REG, GT_TP3_REG, GT_TP4_REG, GT_TP5_REG};
// 扫描触摸屏(采用查询方式)
// mode:0,正常扫描.
// 返回值:当前触屏状态.
// 0,触屏无触摸;1,触屏有触摸
uint8_t GT9147_Scan(uint8_t mode)
{
  uint8_t buf[4];
  uint8_t i   = 0;
  uint8_t res = 0;
  uint8_t temp;
  uint8_t tempsta;
  static uint8_t t = 0; // 控制查询间隔,从而降低CPU占用率
  t++;

  if (!((t % 10) == 0 || t < 10)) return res; // 空闲时,每进入10次CTP_Scan函数才检测1次,从而节省CPU使用率

  GT9147_RD_Reg(GT_GSTID_REG, &mode, 1); // 读取触摸点的状态
  if (mode & 0X80 && ((mode & 0XF) < 6)) {
    temp = 0;
    GT9147_WR_Reg(GT_GSTID_REG, &temp, 1); // 清标志
  }
  if ((mode & 0XF) && ((mode & 0XF) < 6)) {
    temp        = 0XFF << (mode & 0XF); // 将点的个数转换为1的位数,匹配tp_dev.sta定义
    tempsta     = tp_dev.sta;           // 保存当前的tp_dev.sta值
    tp_dev.sta  = (~temp) | TP_PRES_DOWN | TP_CATH_PRES;
    tp_dev.x[4] = tp_dev.x[0]; // 保存触点0的数据
    tp_dev.y[4] = tp_dev.y[0];
    for (i = 0; i < 5; i++) {
      if (!(tp_dev.sta & (1 << i))) continue; // 触摸有效?

      GT9147_RD_Reg(GT9147_TPX_TBL[i], buf, 4); // 读取XY坐标值

      if (tp_dev.touchtype & 0X01) // 横屏
      {
        tp_dev.y[i] = ((uint16_t)buf[1] << 8) + buf[0];
        tp_dev.x[i] = 800 - (((uint16_t)buf[3] << 8) + buf[2]);
      } else {
        tp_dev.x[i] = ((uint16_t)buf[1] << 8) + buf[0];
        tp_dev.y[i] = ((uint16_t)buf[3] << 8) + buf[2];
      }
    }
    res = 1; //success
    if (tp_dev.x[0] > lcddev.width || tp_dev.y[0] > lcddev.height) // 非法数据(坐标超出了)
    {
      if ((mode & 0XF) > 1) // 有其他点有数据,则复第二个触点的数据到第一个触点.
      {
        tp_dev.x[0] = tp_dev.x[1];
        tp_dev.y[0] = tp_dev.y[1];
        t           = 0; // 触发一次,则会最少连续监测10次,从而提高命中率
      } else             // 非法数据,则忽略此次数据(还原原来的)
      {
        tp_dev.x[0] = tp_dev.x[4];
        tp_dev.y[0] = tp_dev.y[4];
        mode        = 0X80;
        tp_dev.sta  = tempsta; // 恢复tp_dev.sta
      }
    } else
      t = 0; // 触发一次,则会最少连续监测10次,从而提高命中率
  }
  if ((mode & 0X8F) == 0X80) // 无触摸点按下
  {
    if (tp_dev.sta & TP_PRES_DOWN) // 之前是被按下的
    {
      tp_dev.sta &= ~(1 << 7); // 标记按键松开
    } else                     // 之前就没有被按下
    {
      tp_dev.x[0] = 0xffff;
      tp_dev.y[0] = 0xffff;
      tp_dev.sta &= 0XE0; // 清除点有效标记
    }
  }
  if (t > 240) t = 10; // 重新从10开始计数
  return res;
}